Desarrollo y
resultados
Antes de comenzar, instalamos y/o cargamos todos los paquetes
requeridos.
if (!require("pacman")) install.packages("pacman")
library(pacman)
p_load(imager, wavethresh, ggplot2, dplyr, SpatialPack, waveslim, EBImage, stringr, jpeg)
Comenzamos cargando y visualizando las fotografías a emplear.
images_path <- list.files("./fotos", full.names = TRUE)
nombres_images <- str_remove_all(string = str_remove_all(string = images_path, pattern = "./fotos/"), pattern = "\\.JPG|\\.jpg")
images <- lapply(images_path, readJPEG) # Cargamos las imágenes
# Nota: cambie de load.image a readJPEG pq asi ya no hace falta usar el drop para quitar lo de cimg, sale bien directamente
names(images) <- nombres_images
# Rotamos algunas de las fotos para una visualización más uniforme
fotos_a_girar <- c("1", "2", "3", "4")
images_rotadas <- lapply(images[fotos_a_girar], aperm, perm = c(2, 1, 3))
for (i in fotos_a_girar) {
images_rotadas[[i]] < images_rotadas[[i]][dim(images_rotadas[[i]])[1]:1, , ]
}
images[fotos_a_girar] <- images_rotadas
rm(images_rotadas)
rm(fotos_a_girar)
# Visualizamos las imagenes originales
par(mfrow = c(2, 3), mar = c(1, 1, 1, 1))
for (img in nombres_images) {
display(Image(images[[img]], colormode = "Color"), method = "r")
}

Inclusión de ruido
sintético en las imágenes
# Definición de tipos de ruido
NOISE_TYPES <- list(
gaussian = list(
generator = function(channel, params) {
# Desviación estándar del ruido con un valor predeterminado
noise_std_dev <- params$std_dev %||% 0.5
# Generación de ruido gaussiano
noise <- array(
rnorm(length(channel), mean = 0, sd = noise_std_dev),
dim = dim(channel)
)
# Asegurar que los valores estén entre 0 y 1
pmax(0, pmin(1, channel + noise))
}
),
sinusoidal_high = list(
generator = function(channel, params) {
# Frecuencia y amplitud del ruido sinusoidal de alta frecuencia
frequency <- params$frequency %||% 25
amplitude <- params$amplitude %||% 0.2
# Generación de ruido sinusoidal
height <- dim(channel)[1]
width <- dim(channel)[2]
x <- seq(0, 2 * pi, length.out = width)
y <- seq(0, 2 * pi, length.out = height)
noise_grid <- outer(sin(x * frequency), sin(y * frequency))
# Aplicar el ruido
noise <- array(noise_grid * amplitude, dim = dim(channel))
pmax(0, pmin(1, channel + noise))
}
),
sinusoidal_low = list(
generator = function(channel, params) {
# Frecuencia y amplitud del ruido sinusoidal de baja frecuencia
frequency <- params$frequency %||% 2
amplitude <- params$amplitude %||% 0.2
# Generación de ruido sinusoidal
height <- dim(channel)[1]
width <- dim(channel)[2]
x <- seq(0, 2 * pi, length.out = width)
y <- seq(0, 2 * pi, length.out = height)
noise_grid <- outer(sin(x * frequency), sin(y * frequency))
# Aplicar el ruido
noise <- array(noise_grid * amplitude, dim = dim(channel))
pmax(0, pmin(1, channel + noise))
}
),
salt_pepper = list(
generator = function(channel, params) {
# Proporción de píxeles afectados por el ruido de sal y pimienta
epsilon <- params$epsilon %||% 0.2
# Generación de ruido
noise <- matrix(sample(c(0, 1, NA), length(channel), replace = TRUE, prob = c(epsilon / 2, epsilon / 2, 1 - epsilon)),
nrow = dim(channel)[1], ncol = dim(channel)[2]
)
channel[!is.na(noise)] <- noise[!is.na(noise)]
channel
}
),
gamma = list(
generator = function(channel, params) {
# Ruido multiplicativo gamma con parámetro de dispersión
looks <- params$looks %||% 2
noise <- array(rgamma(length(channel), shape = looks, scale = 1 / looks), dim = dim(channel))
pmax(0, pmin(1, channel * noise))
}
),
uniform_multiplicative = list(
generator = function(channel, params) {
# Ruido multiplicativo uniforme
looks <- params$looks %||% 2
noise_channel <- SpatialPack::imnoise(
img = channel,
type = "speckle",
looks = looks
)
pmax(0, pmin(1, noise_channel))
}
)
)
# Función para añadir ruido a una imagen
add_noise_to_image <- function(image_name, noise_type, noise_params = list(),plot=FALSE) {
# Verificar si la imagen existe en la lista
if (!image_name %in% names(images)) {
stop("La imagen con este nombre no se encuentra en la lista 'images'")
}
# Verificar el tipo de ruido
if (!noise_type %in% names(NOISE_TYPES)) {
stop(
"El tipo de ruido es desconocido. Tipos disponibles: ",
paste(names(NOISE_TYPES), collapse = ", ")
)
}
# Obtener la imagen original de la lista
original_image <- images[[image_name]]
# Convertir la imagen a un array si es necesario
image_array <- as.array(original_image)
# Aplicar ruido a cada canal
noisy_channels <- lapply(1:3, function(i) {
channel <- image_array[, , i]
NOISE_TYPES[[noise_type]]$generator(channel, noise_params)
})
# Crear la imagen con ruido
noisy_image_array <- array(
unlist(noisy_channels),
dim = dim(image_array)
)
# Visualizar si se ha indicado
if (plot == TRUE){
layout(matrix(1:2, 1, 2))
plot(Image(original_image, colormode = "Color"))
title("Original")
plot(Image((noisy_image_array), colormode = "Color"))
title(paste("Ruido:", noise_type))}
return(noisy_image_array)
}
# Aplicar los diferentes tipos de ruido a cada imagen
#add_noise_to_image("1", "gaussian", list(std_dev = 0.3))
#add_noise_to_image("2", "sinusoidal_high", list(frequency = 25, amplitude = 0.2))
#add_noise_to_image("3", "sinusoidal_low", list(frequency = 2, amplitude = 0.2))
#add_noise_to_image("4", "salt_pepper", list(epsilon = 0.1))
#add_noise_to_image("5", "gamma", list(looks = 2))
#add_noise_to_image("5", "uniform_multiplicative", list(looks = 2))
Función imwd
Función para hacer cuadrada la imagen.
Para aplicar el algoritmo de Deformación Iterativa de Mallat (IMWD),
es necesario que la imagen tenga una forma cuadrada. Dado que muchas
imágenes no son cuadradas, es necesario convertirlas antes de aplicar el
algoritmo.
A continuación, se presenta una función de pre-procesamiento, que
ajusta cualquier imagen rectangular a un tamaño cuadrado, manteniendo
sus proporciones originales al agregar relleno (fondo blanco) si es
necesario. Esta transformación asegura que la imagen sea compatible con
el algoritmo IMWD.
hacer_cuadrada_potencia_2 <- function(imagen) {
n_filas <- dim(imagen)[1]
n_columnas <- dim(imagen)[2]
nuevo_tamano <- max(n_filas, n_columnas)
siguiente_potencia_2 <- 2^ceiling(log2(nuevo_tamano))
imagen_cuadrada <- array(0, dim = c(siguiente_potencia_2, siguiente_potencia_2, dim(imagen)[3]))
imagen_cuadrada[1:n_filas, 1:n_columnas, ] <- imagen
return(imagen_cuadrada)
}
Cargar imagenes en la función cuadrada
Aplicamos la función en 3 fotos
fotos_cuadradas <- lapply(imagen_noise, hacer_cuadrada_potencia_2)
Imagen Original

[1] "1 Noise: gaussian"
[1] "2 Noise: gamma"

[1] "3 Noise: unif"


Cuando uso fotos de mayor calidad solo acepta hasta 2, con 3 aparece
esto: Error: vector memory limit of 16.0 Gb reached, see
mem.maxVSize()
El umbral “universal”, propuesta por Donoho y Johnstone, es un método
utilizado en la eliminación de ruido en señales e imágenes mediante
transformadas de wavelet. Esta estrategia calcula el umbral aplicado a
los coeficientes de wavelet en función del tamaño de la señal y una
estimación del nivel de ruido. La fórmula del umbral “universal” es
\[ \sigma \sqrt{2 \log n}\] donde
\(\sigma\) es una estimación del ruido
y n es el número de muestras o elementos de la señal.
Imagenes sin ruido
Error in slot(prueba, ".Data")[1:1600, 1:1066, , drop = FALSE] :
subscript out of bounds
Ruido gaussiano
Imagen con ruido Gaussiano original
Imagen Cortada sin filtro
Ajusta el contraste de la imagen, incrementando la diferencia
entre los píxeles claros y oscuros.
Imagen con filtro paso alto
Función denoise…
LS0tCnRpdGxlOiAiRWxpbWluYWNpw7NuIGRlIHJ1aWRvIGVuIGltw6FnZW5lcyBjb24gd2F2ZWxldHMuIgpzdWJ0aXRsZTogIkFuw6FsaXNpcyBkZSBzZcOxYWxlcyIKYXV0aG9yOiAiR3J1cG8gRTogQWxlamFuZHJhIFZlbmVnYXMsIFJlYmVjYSBDb21wYW55LCBNYXJ0YSBNZWRpbmEsIEFsZWphbmRybyBDb3JuZWxpbyB5IElsaWEgWmhpZ2FyZXYuIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZWNobzogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogbHVtZW4KICAgIHRvYzogdHJ1ZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICBib29rZG93bjo6cGRmX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGZpZ3VyZV9jYXB0aW9uOiAiRmlndXJhIiAjIFJlZmVyZW5jaWFzIGVuIGNhc3RlbGxhbm8KICAgIHRhYmxlX2NhcHRpb246ICJUYWJsYSIKICBodG1sX25vdGVib29rOgogICAgZWNobzogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICBlY2hvOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgdG9jOiB0cnVlCiAgICBmaWd1cmVfY2FwdGlvbjogIkZpZ3VyYSIKICAgIHRhYmxlX2NhcHRpb246ICJUYWJsYSIKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUKcGFyYW1zOgogIGxhbmc6IEVTCmxhbmc6ICJgciBzd2l0Y2gocGFyYW1zJGxhbmcsIEVTID0gJ2VzLUVTJywgRU4gPSAnZW4tVVMnKWAiCmxhbmd1YWdlOgogIGxhYmVsOgogICAgZmlnOiAnRmlndXJhICcKICAgIHRhYjogJ1RhYmxhICcKICAgIGVxOiAnRWN1YWNpw7NuICcKICAgIHRobTogJ1Rlb3JlbWEgJwogICAgbGVtOiAnTGVtYSAnCiAgICBkZWY6ICdEZWZpbmljacOzbiAnCiAgICBjb3I6ICdDb3JvbGFyaW8gJwogICAgcHJwOiAnUHJvcG9zaWNpw7NuICcKICAgIGV4bTogJ0VqZW1wbG8gJwogICAgZXhyOiAnRWplcmNpY2lvICcKICAgIHByb29mOiAnRGVtb3N0cmFjacOzbi4gJwogICAgcmVtYXJrOiAnTm90YTogJwogICAgc29sdXRpb246ICdTb2x1Y2nDs24uICcKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpybShsaXN0ID0gbHMoKSkKYGBgCgojIEludHJvZHVjY2nDs24KCiMgRnVuZGFtZW50byB0ZcOzcmljbzogZWxpbWluYWNpw7NuIGRlIHJ1aWRvIGNvbiB3YXZlbGV0cwoKIyBGdW5jaW9uZXMgZGUgcHJvZ3JhbWFjaW9uIGVtcGxlYWRhcwoKIyBEZXNhcnJvbGxvIHkgcmVzdWx0YWRvcwoKQW50ZXMgZGUgY29tZW56YXIsIGluc3RhbGFtb3MgeS9vIGNhcmdhbW9zIHRvZG9zIGxvcyBwYXF1ZXRlcyByZXF1ZXJpZG9zLgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQpsaWJyYXJ5KHBhY21hbikKcF9sb2FkKGltYWdlciwgd2F2ZXRocmVzaCwgZ2dwbG90MiwgZHBseXIsIFNwYXRpYWxQYWNrLCB3YXZlc2xpbSwgRUJJbWFnZSwgc3RyaW5nciwganBlZykKYGBgCgpDb21lbnphbW9zIGNhcmdhbmRvIHkgdmlzdWFsaXphbmRvIGxhcyBmb3RvZ3JhZsOtYXMgYSBlbXBsZWFyLgpgYGB7cn0KaW1hZ2VzX3BhdGggPC0gbGlzdC5maWxlcygiLi9mb3RvcyIsIGZ1bGwubmFtZXMgPSBUUlVFKQoKbm9tYnJlc19pbWFnZXMgPC0gc3RyX3JlbW92ZV9hbGwoc3RyaW5nID0gc3RyX3JlbW92ZV9hbGwoc3RyaW5nID0gaW1hZ2VzX3BhdGgsIHBhdHRlcm4gPSAiLi9mb3Rvcy8iKSwgcGF0dGVybiA9ICJcXC5KUEd8XFwuanBnIikKCmltYWdlcyA8LSBsYXBwbHkoaW1hZ2VzX3BhdGgsIHJlYWRKUEVHKSAjIENhcmdhbW9zIGxhcyBpbcOhZ2VuZXMKCiMgTm90YTogY2FtYmllIGRlIGxvYWQuaW1hZ2UgYSByZWFkSlBFRyBwcSBhc2kgeWEgbm8gaGFjZSBmYWx0YSB1c2FyIGVsIGRyb3AgcGFyYSBxdWl0YXIgbG8gZGUgY2ltZywgc2FsZSBiaWVuIGRpcmVjdGFtZW50ZQpuYW1lcyhpbWFnZXMpIDwtIG5vbWJyZXNfaW1hZ2VzCmBgYAoKYGBge3J9CiMgUm90YW1vcyBhbGd1bmFzIGRlIGxhcyBmb3RvcyBwYXJhIHVuYSB2aXN1YWxpemFjacOzbiBtw6FzIHVuaWZvcm1lCmZvdG9zX2FfZ2lyYXIgPC0gYygiMSIsICIyIiwgIjMiLCAiNCIpCmltYWdlc19yb3RhZGFzIDwtIGxhcHBseShpbWFnZXNbZm90b3NfYV9naXJhcl0sIGFwZXJtLCBwZXJtID0gYygyLCAxLCAzKSkKCgpmb3IgKGkgaW4gZm90b3NfYV9naXJhcikgewogIGltYWdlc19yb3RhZGFzW1tpXV0gPCBpbWFnZXNfcm90YWRhc1tbaV1dW2RpbShpbWFnZXNfcm90YWRhc1tbaV1dKVsxXToxLCAsIF0KfQoKaW1hZ2VzW2ZvdG9zX2FfZ2lyYXJdIDwtIGltYWdlc19yb3RhZGFzCnJtKGltYWdlc19yb3RhZGFzKQpybShmb3Rvc19hX2dpcmFyKQpgYGAKCgpgYGB7cn0KIyBWaXN1YWxpemFtb3MgbGFzIGltYWdlbmVzIG9yaWdpbmFsZXMKCnBhcihtZnJvdyA9IGMoMiwgMyksIG1hciA9IGMoMSwgMSwgMSwgMSkpCgpmb3IgKGltZyBpbiBub21icmVzX2ltYWdlcykgewogIGRpc3BsYXkoSW1hZ2UoaW1hZ2VzW1tpbWddXSwgY29sb3Jtb2RlID0gIkNvbG9yIiksIG1ldGhvZCA9ICJyIikKfQpgYGAKCiMjIEluY2x1c2nDs24gZGUgcnVpZG8gc2ludMOpdGljbyBlbiBsYXMgaW3DoWdlbmVzCgpgYGB7ciwgZmlnLmhlaWdodCA9IDR9CiMgRGVmaW5pY2nDs24gZGUgdGlwb3MgZGUgcnVpZG8KCk5PSVNFX1RZUEVTIDwtIGxpc3QoCiAgZ2F1c3NpYW4gPSBsaXN0KAogICAgZ2VuZXJhdG9yID0gZnVuY3Rpb24oY2hhbm5lbCwgcGFyYW1zKSB7CiAgICAgICMgRGVzdmlhY2nDs24gZXN0w6FuZGFyIGRlbCBydWlkbyBjb24gdW4gdmFsb3IgcHJlZGV0ZXJtaW5hZG8KICAgICAgbm9pc2Vfc3RkX2RldiA8LSBwYXJhbXMkc3RkX2RldiAlfHwlIDAuNQoKICAgICAgIyBHZW5lcmFjacOzbiBkZSBydWlkbyBnYXVzc2lhbm8KICAgICAgbm9pc2UgPC0gYXJyYXkoCiAgICAgICAgcm5vcm0obGVuZ3RoKGNoYW5uZWwpLCBtZWFuID0gMCwgc2QgPSBub2lzZV9zdGRfZGV2KSwKICAgICAgICBkaW0gPSBkaW0oY2hhbm5lbCkKICAgICAgKQoKICAgICAgIyBBc2VndXJhciBxdWUgbG9zIHZhbG9yZXMgZXN0w6luIGVudHJlIDAgeSAxCiAgICAgIHBtYXgoMCwgcG1pbigxLCBjaGFubmVsICsgbm9pc2UpKQogICAgfQogICksCiAgc2ludXNvaWRhbF9oaWdoID0gbGlzdCgKICAgIGdlbmVyYXRvciA9IGZ1bmN0aW9uKGNoYW5uZWwsIHBhcmFtcykgewogICAgICAjIEZyZWN1ZW5jaWEgeSBhbXBsaXR1ZCBkZWwgcnVpZG8gc2ludXNvaWRhbCBkZSBhbHRhIGZyZWN1ZW5jaWEKICAgICAgZnJlcXVlbmN5IDwtIHBhcmFtcyRmcmVxdWVuY3kgJXx8JSAyNQogICAgICBhbXBsaXR1ZGUgPC0gcGFyYW1zJGFtcGxpdHVkZSAlfHwlIDAuMgoKICAgICAgIyBHZW5lcmFjacOzbiBkZSBydWlkbyBzaW51c29pZGFsCiAgICAgIGhlaWdodCA8LSBkaW0oY2hhbm5lbClbMV0KICAgICAgd2lkdGggPC0gZGltKGNoYW5uZWwpWzJdCiAgICAgIHggPC0gc2VxKDAsIDIgKiBwaSwgbGVuZ3RoLm91dCA9IHdpZHRoKQogICAgICB5IDwtIHNlcSgwLCAyICogcGksIGxlbmd0aC5vdXQgPSBoZWlnaHQpCiAgICAgIG5vaXNlX2dyaWQgPC0gb3V0ZXIoc2luKHggKiBmcmVxdWVuY3kpLCBzaW4oeSAqIGZyZXF1ZW5jeSkpCgogICAgICAjIEFwbGljYXIgZWwgcnVpZG8KICAgICAgbm9pc2UgPC0gYXJyYXkobm9pc2VfZ3JpZCAqIGFtcGxpdHVkZSwgZGltID0gZGltKGNoYW5uZWwpKQogICAgICBwbWF4KDAsIHBtaW4oMSwgY2hhbm5lbCArIG5vaXNlKSkKICAgIH0KICApLAogIHNpbnVzb2lkYWxfbG93ID0gbGlzdCgKICAgIGdlbmVyYXRvciA9IGZ1bmN0aW9uKGNoYW5uZWwsIHBhcmFtcykgewogICAgICAjIEZyZWN1ZW5jaWEgeSBhbXBsaXR1ZCBkZWwgcnVpZG8gc2ludXNvaWRhbCBkZSBiYWphIGZyZWN1ZW5jaWEKICAgICAgZnJlcXVlbmN5IDwtIHBhcmFtcyRmcmVxdWVuY3kgJXx8JSAyCiAgICAgIGFtcGxpdHVkZSA8LSBwYXJhbXMkYW1wbGl0dWRlICV8fCUgMC4yCgogICAgICAjIEdlbmVyYWNpw7NuIGRlIHJ1aWRvIHNpbnVzb2lkYWwKICAgICAgaGVpZ2h0IDwtIGRpbShjaGFubmVsKVsxXQogICAgICB3aWR0aCA8LSBkaW0oY2hhbm5lbClbMl0KICAgICAgeCA8LSBzZXEoMCwgMiAqIHBpLCBsZW5ndGgub3V0ID0gd2lkdGgpCiAgICAgIHkgPC0gc2VxKDAsIDIgKiBwaSwgbGVuZ3RoLm91dCA9IGhlaWdodCkKICAgICAgbm9pc2VfZ3JpZCA8LSBvdXRlcihzaW4oeCAqIGZyZXF1ZW5jeSksIHNpbih5ICogZnJlcXVlbmN5KSkKCiAgICAgICMgQXBsaWNhciBlbCBydWlkbwogICAgICBub2lzZSA8LSBhcnJheShub2lzZV9ncmlkICogYW1wbGl0dWRlLCBkaW0gPSBkaW0oY2hhbm5lbCkpCiAgICAgIHBtYXgoMCwgcG1pbigxLCBjaGFubmVsICsgbm9pc2UpKQogICAgfQogICksCiAgc2FsdF9wZXBwZXIgPSBsaXN0KAogICAgZ2VuZXJhdG9yID0gZnVuY3Rpb24oY2hhbm5lbCwgcGFyYW1zKSB7CiAgICAgICMgUHJvcG9yY2nDs24gZGUgcMOteGVsZXMgYWZlY3RhZG9zIHBvciBlbCBydWlkbyBkZSBzYWwgeSBwaW1pZW50YQogICAgICBlcHNpbG9uIDwtIHBhcmFtcyRlcHNpbG9uICV8fCUgMC4yCgogICAgICAjIEdlbmVyYWNpw7NuIGRlIHJ1aWRvCiAgICAgIG5vaXNlIDwtIG1hdHJpeChzYW1wbGUoYygwLCAxLCBOQSksIGxlbmd0aChjaGFubmVsKSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKGVwc2lsb24gLyAyLCBlcHNpbG9uIC8gMiwgMSAtIGVwc2lsb24pKSwKICAgICAgICBucm93ID0gZGltKGNoYW5uZWwpWzFdLCBuY29sID0gZGltKGNoYW5uZWwpWzJdCiAgICAgICkKICAgICAgY2hhbm5lbFshaXMubmEobm9pc2UpXSA8LSBub2lzZVshaXMubmEobm9pc2UpXQogICAgICBjaGFubmVsCiAgICB9CiAgKSwKICBnYW1tYSA9IGxpc3QoCiAgICBnZW5lcmF0b3IgPSBmdW5jdGlvbihjaGFubmVsLCBwYXJhbXMpIHsKICAgICAgIyBSdWlkbyBtdWx0aXBsaWNhdGl2byBnYW1tYSBjb24gcGFyw6FtZXRybyBkZSBkaXNwZXJzacOzbgogICAgICBsb29rcyA8LSBwYXJhbXMkbG9va3MgJXx8JSAyCiAgICAgIG5vaXNlIDwtIGFycmF5KHJnYW1tYShsZW5ndGgoY2hhbm5lbCksIHNoYXBlID0gbG9va3MsIHNjYWxlID0gMSAvIGxvb2tzKSwgZGltID0gZGltKGNoYW5uZWwpKQogICAgICBwbWF4KDAsIHBtaW4oMSwgY2hhbm5lbCAqIG5vaXNlKSkKICAgIH0KICApLAogIHVuaWZvcm1fbXVsdGlwbGljYXRpdmUgPSBsaXN0KAogICAgZ2VuZXJhdG9yID0gZnVuY3Rpb24oY2hhbm5lbCwgcGFyYW1zKSB7CiAgICAgICMgUnVpZG8gbXVsdGlwbGljYXRpdm8gdW5pZm9ybWUKICAgICAgbG9va3MgPC0gcGFyYW1zJGxvb2tzICV8fCUgMgogICAgICBub2lzZV9jaGFubmVsIDwtIFNwYXRpYWxQYWNrOjppbW5vaXNlKAogICAgICAgIGltZyA9IGNoYW5uZWwsCiAgICAgICAgdHlwZSA9ICJzcGVja2xlIiwKICAgICAgICBsb29rcyA9IGxvb2tzCiAgICAgICkKICAgICAgcG1heCgwLCBwbWluKDEsIG5vaXNlX2NoYW5uZWwpKQogICAgfQogICkKKQoKIyBGdW5jacOzbiBwYXJhIGHDsWFkaXIgcnVpZG8gYSB1bmEgaW1hZ2VuCmFkZF9ub2lzZV90b19pbWFnZSA8LSBmdW5jdGlvbihpbWFnZV9uYW1lLCBub2lzZV90eXBlLCBub2lzZV9wYXJhbXMgPSBsaXN0KCkscGxvdD1GQUxTRSkgewogICMgVmVyaWZpY2FyIHNpIGxhIGltYWdlbiBleGlzdGUgZW4gbGEgbGlzdGEKICBpZiAoIWltYWdlX25hbWUgJWluJSBuYW1lcyhpbWFnZXMpKSB7CiAgICBzdG9wKCJMYSBpbWFnZW4gY29uIGVzdGUgbm9tYnJlIG5vIHNlIGVuY3VlbnRyYSBlbiBsYSBsaXN0YSAnaW1hZ2VzJyIpCiAgfQoKICAjIFZlcmlmaWNhciBlbCB0aXBvIGRlIHJ1aWRvCiAgaWYgKCFub2lzZV90eXBlICVpbiUgbmFtZXMoTk9JU0VfVFlQRVMpKSB7CiAgICBzdG9wKAogICAgICAiRWwgdGlwbyBkZSBydWlkbyBlcyBkZXNjb25vY2lkby4gVGlwb3MgZGlzcG9uaWJsZXM6ICIsCiAgICAgIHBhc3RlKG5hbWVzKE5PSVNFX1RZUEVTKSwgY29sbGFwc2UgPSAiLCAiKQogICAgKQogIH0KCiAgIyBPYnRlbmVyIGxhIGltYWdlbiBvcmlnaW5hbCBkZSBsYSBsaXN0YQogIG9yaWdpbmFsX2ltYWdlIDwtIGltYWdlc1tbaW1hZ2VfbmFtZV1dCgogICMgQ29udmVydGlyIGxhIGltYWdlbiBhIHVuIGFycmF5IHNpIGVzIG5lY2VzYXJpbwogIGltYWdlX2FycmF5IDwtIGFzLmFycmF5KG9yaWdpbmFsX2ltYWdlKQoKICAjIEFwbGljYXIgcnVpZG8gYSBjYWRhIGNhbmFsCiAgbm9pc3lfY2hhbm5lbHMgPC0gbGFwcGx5KDE6MywgZnVuY3Rpb24oaSkgewogICAgY2hhbm5lbCA8LSBpbWFnZV9hcnJheVssICwgaV0KICAgIE5PSVNFX1RZUEVTW1tub2lzZV90eXBlXV0kZ2VuZXJhdG9yKGNoYW5uZWwsIG5vaXNlX3BhcmFtcykKICB9KQoKICAjIENyZWFyIGxhIGltYWdlbiBjb24gcnVpZG8KICBub2lzeV9pbWFnZV9hcnJheSA8LSBhcnJheSgKICAgIHVubGlzdChub2lzeV9jaGFubmVscyksCiAgICBkaW0gPSBkaW0oaW1hZ2VfYXJyYXkpCiAgKQoKICMgVmlzdWFsaXphciBzaSBzZSBoYSBpbmRpY2FkbwogIGlmIChwbG90ID09IFRSVUUpewogIGxheW91dChtYXRyaXgoMToyLCAxLCAyKSkKICBwbG90KEltYWdlKG9yaWdpbmFsX2ltYWdlLCBjb2xvcm1vZGUgPSAiQ29sb3IiKSkKICB0aXRsZSgiT3JpZ2luYWwiKQogIHBsb3QoSW1hZ2UoKG5vaXN5X2ltYWdlX2FycmF5KSwgY29sb3Jtb2RlID0gIkNvbG9yIikpCiAgdGl0bGUocGFzdGUoIlJ1aWRvOiIsIG5vaXNlX3R5cGUpKX0KICAKICByZXR1cm4obm9pc3lfaW1hZ2VfYXJyYXkpCn0KCmBgYAoKCmBgYHtyfQoKIyBBcGxpY2FyIGxvcyBkaWZlcmVudGVzIHRpcG9zIGRlIHJ1aWRvIGEgY2FkYSBpbWFnZW4KI2FkZF9ub2lzZV90b19pbWFnZSgiMSIsICJnYXVzc2lhbiIsIGxpc3Qoc3RkX2RldiA9IDAuMykpCiNhZGRfbm9pc2VfdG9faW1hZ2UoIjIiLCAic2ludXNvaWRhbF9oaWdoIiwgbGlzdChmcmVxdWVuY3kgPSAyNSwgYW1wbGl0dWRlID0gMC4yKSkKI2FkZF9ub2lzZV90b19pbWFnZSgiMyIsICJzaW51c29pZGFsX2xvdyIsIGxpc3QoZnJlcXVlbmN5ID0gMiwgYW1wbGl0dWRlID0gMC4yKSkKI2FkZF9ub2lzZV90b19pbWFnZSgiNCIsICJzYWx0X3BlcHBlciIsIGxpc3QoZXBzaWxvbiA9IDAuMSkpCiNhZGRfbm9pc2VfdG9faW1hZ2UoIjUiLCAiZ2FtbWEiLCBsaXN0KGxvb2tzID0gMikpCiNhZGRfbm9pc2VfdG9faW1hZ2UoIjUiLCAidW5pZm9ybV9tdWx0aXBsaWNhdGl2ZSIsIGxpc3QobG9va3MgPSAyKSkKYGBgCgoKIyMgRnVuY2nDs24gaW13ZAoKYGBge3IsZWNobz1GQUxTRX0KIyBhZ3JlZ2FyIHJ1aWRvIGEgaW1hZ2VuZXMKZ2F1c3NpYW5fbm9pc2VfNTwtYWRkX25vaXNlX3RvX2ltYWdlKCI1IiwgImdhdXNzaWFuIiwgbGlzdChzdGRfZGV2ID0gMC41KSkKZ2FtbWFfbm9pc2VfNTwtYWRkX25vaXNlX3RvX2ltYWdlKCI1IiwgImdhbW1hIiwgbGlzdChsb29rcyA9IDIpKQp1bmlmX25vaXNlXzU8LWFkZF9ub2lzZV90b19pbWFnZSgiNSIsICJ1bmlmb3JtX211bHRpcGxpY2F0aXZlIiwgbGlzdChsb29rcyA9IDIpKQpgYGAKCgoKYGBge3IsZWNobz1GQUxTRX0KI3Byb2JhciBjb24gMyAKaW1hZ2VuX25vaXNlIDwtIGxpc3QoZ2F1c3NpYW5fbm9pc2VfNSwgZ2FtbWFfbm9pc2VfNSwgdW5pZl9ub2lzZV81KQpuYW1lcyhpbWFnZW5fbm9pc2UpIDwtIGMoJzEgTm9pc2U6IGdhdXNzaWFuJywgJzIgTm9pc2U6IGdhbW1hJywgJzMgTm9pc2U6IHVuaWYnKQoKYGBgCgoKKipGdW5jacOzbiBwYXJhIGhhY2VyIGN1YWRyYWRhIGxhIGltYWdlbi4qKgoKUGFyYSBhcGxpY2FyIGVsIGFsZ29yaXRtbyBkZSBEZWZvcm1hY2nDs24gSXRlcmF0aXZhIGRlIE1hbGxhdCAoSU1XRCksIGVzIG5lY2VzYXJpbyBxdWUgbGEgaW1hZ2VuIHRlbmdhIHVuYSBmb3JtYSBjdWFkcmFkYS4gRGFkbyBxdWUgbXVjaGFzIGltw6FnZW5lcyBubyBzb24gY3VhZHJhZGFzLCBlcyBuZWNlc2FyaW8gY29udmVydGlybGFzIGFudGVzIGRlIGFwbGljYXIgZWwgYWxnb3JpdG1vLgoKQSBjb250aW51YWNpw7NuLCBzZSBwcmVzZW50YSB1bmEgZnVuY2nDs24gZGUgcHJlLXByb2Nlc2FtaWVudG8sIHF1ZSBhanVzdGEgY3VhbHF1aWVyIGltYWdlbiByZWN0YW5ndWxhciBhIHVuIHRhbWHDsW8gY3VhZHJhZG8sIG1hbnRlbmllbmRvIHN1cyBwcm9wb3JjaW9uZXMgb3JpZ2luYWxlcyBhbCBhZ3JlZ2FyIHJlbGxlbm8gKGZvbmRvIGJsYW5jbykgc2kgZXMgbmVjZXNhcmlvLiBFc3RhIHRyYW5zZm9ybWFjacOzbiBhc2VndXJhIHF1ZSBsYSBpbWFnZW4gc2VhIGNvbXBhdGlibGUgY29uIGVsIGFsZ29yaXRtbyBJTVdELgoKYGBge3J9CmhhY2VyX2N1YWRyYWRhX3BvdGVuY2lhXzIgPC0gZnVuY3Rpb24oaW1hZ2VuKSB7CiAgbl9maWxhcyA8LSBkaW0oaW1hZ2VuKVsxXQogIG5fY29sdW1uYXMgPC0gZGltKGltYWdlbilbMl0KICAKICBudWV2b190YW1hbm8gPC0gbWF4KG5fZmlsYXMsIG5fY29sdW1uYXMpCiAgCiAgc2lndWllbnRlX3BvdGVuY2lhXzIgPC0gMl5jZWlsaW5nKGxvZzIobnVldm9fdGFtYW5vKSkKICAKICBpbWFnZW5fY3VhZHJhZGEgPC0gYXJyYXkoMCwgZGltID0gYyhzaWd1aWVudGVfcG90ZW5jaWFfMiwgc2lndWllbnRlX3BvdGVuY2lhXzIsIGRpbShpbWFnZW4pWzNdKSkgCiAgCiAgaW1hZ2VuX2N1YWRyYWRhWzE6bl9maWxhcywgMTpuX2NvbHVtbmFzLCBdIDwtIGltYWdlbgogIAogIHJldHVybihpbWFnZW5fY3VhZHJhZGEpCn0KCmBgYAoKCkNhcmdhciBpbWFnZW5lcyBlbiBsYSBmdW5jacOzbiBjdWFkcmFkYQoKCgpBcGxpY2Ftb3MgbGEgZnVuY2nDs24gZW4gMyBmb3RvcwpgYGB7cn0KZm90b3NfY3VhZHJhZGFzIDwtIGxhcHBseShpbWFnZW5fbm9pc2UsIGhhY2VyX2N1YWRyYWRhX3BvdGVuY2lhXzIpCmBgYAoKSW1hZ2VuIE9yaWdpbmFsCgpgYGB7cixlY2hvPUZBTFNFfQppPTUKRUJJbWFnZTo6ZGlzcGxheShJbWFnZShpbWFnZXNbW2ldXSwgY29sb3Jtb2RlID0gJ0NvbG9yJyksIG1ldGhvZCA9ICdyJykKCmBgYAoKCgpgYGB7cixlY2hvPUZBTFNFfQpmb3IoaSBpbiBjKDE6MykpewpwcmludChuYW1lcyhmb3Rvc19jdWFkcmFkYXMpW2ldKQpwcmludCgnICcpCkVCSW1hZ2U6OmRpc3BsYXkoSW1hZ2UoZm90b3NfY3VhZHJhZGFzW1tpXV0sIGNvbG9ybW9kZSA9ICdDb2xvcicpLCBtZXRob2QgPSAncicpCn0KYGBgCgpDdWFuZG8gdXNvIGZvdG9zIGRlIG1heW9yIGNhbGlkYWQgc29sbyBhY2VwdGEgaGFzdGEgMiwgY29uIDMgYXBhcmVjZSBlc3RvOgpFcnJvcjogdmVjdG9yIG1lbW9yeSBsaW1pdCBvZiAxNi4wIEdiIHJlYWNoZWQsIHNlZSBtZW0ubWF4VlNpemUoKQoKCgpFbCB1bWJyYWwgInVuaXZlcnNhbCIsIHByb3B1ZXN0YSBwb3IgRG9ub2hvIHkgSm9obnN0b25lLCBlcyB1biBtw6l0b2RvIHV0aWxpemFkbyBlbiBsYSBlbGltaW5hY2nDs24gZGUgcnVpZG8gZW4gc2XDsWFsZXMgZSBpbcOhZ2VuZXMgbWVkaWFudGUgdHJhbnNmb3JtYWRhcyBkZSB3YXZlbGV0LiBFc3RhIGVzdHJhdGVnaWEgY2FsY3VsYSBlbCB1bWJyYWwgYXBsaWNhZG8gYSBsb3MgY29lZmljaWVudGVzIGRlIHdhdmVsZXQgZW4gZnVuY2nDs24gZGVsIHRhbWHDsW8gZGUgbGEgc2XDsWFsIHkgdW5hIGVzdGltYWNpw7NuIGRlbCBuaXZlbCBkZSBydWlkby4gTGEgZsOzcm11bGEgZGVsIHVtYnJhbCAidW5pdmVyc2FsIiBlcyAkJCBcc2lnbWEgXHNxcnR7MiBcbG9nIG59JCQgZG9uZGUgJFxzaWdtYSQgZXMgdW5hIGVzdGltYWNpw7NuIGRlbCBydWlkbyB5IG4gZXMgZWwgbsO6bWVybyBkZSBtdWVzdHJhcyBvIGVsZW1lbnRvcyBkZSBsYSBzZcOxYWwuCgoKCgpgYGB7cixlY2hvPUZBTFNFfQpwcm9jZXNhcl9pbWFnZW5fd2F2ZWxldCA8LSBmdW5jdGlvbihmb3RvLCB0aXBvID0gImhhcmQiLCBwb2xpY3kgPSAidW5pdmVyc2FsIikgewogIGx3ZCA8LSBsYXBwbHkoMTozLCBmdW5jdGlvbihjYW5hbCkgewogICAgd2F2ZXRocmVzaDo6aW13ZChmb3RvWywsY2FuYWxdKSAgCiAgfSkKICAKICAjIDIuIEFwbGljYW1vcyBlbCB1bWJyYWwgYSBsb3MgY29lZmljaWVudGVzIGRlIGxhIHRyYW5zZm9ybWFkYSB3YXZlbGV0CiAgbHdkX3RocmVzaG9sZCA8LSBsYXBwbHkobHdkLCBmdW5jdGlvbihjYW5hbF93ZCkgewogICAgbml2ZWxlcyA8LSBjYW5hbF93ZCRubGV2ZWxzCiAgICB3YXZldGhyZXNoOjp0aHJlc2hvbGQoY2FuYWxfd2QsIGxldmVscyA9IDM6KG5pdmVsZXMtMSksIHR5cGUgPSB0aXBvLCBwb2xpY3kgPSBwb2xpY3kpCiAgfSkKICAKICAjIDMuIEFwbGljYW1vcyBsYSB0cmFuc2Zvcm1hZGEgd2F2ZWxldCBpbnZlcnNhIGEgY2FkYSBjYW5hbCB1bWJyYWxpemFkbwogIGlsd2QgPC0gbGFwcGx5KGx3ZF90aHJlc2hvbGQsIGZ1bmN0aW9uKGNhbmFsX3VtYnJhbGl6YWRvKSB7CiAgICB3YXZldGhyZXNoOjppbXdyKGNhbmFsX3VtYnJhbGl6YWRvKSAgIyBUcmFuc2Zvcm1hZGEgd2F2ZWxldCBpbnZlcnNhCiAgfSkKICAKICAjIDQuIFJlY29uc3RydWlyIGxhIGltYWdlbiBjb21iaW5hbmRvIGxvcyB0cmVzIGNhbmFsZXMgcHJvY2VzYWRvcwogIGltYWdlbl9yZWNvbnN0cnVpZGEgPC0gYWJpbmQ6OmFiaW5kKGlsd2RbWzFdXSwgaWx3ZFtbMl1dLCBpbHdkW1szXV0sIGFsb25nID0gMykKICAKICAjIENvbnZlcnRpbW9zIGxhIGltYWdlbiByZWNvbnN0cnVpZGEgZW4gdW4gb2JqZXRvICdJbWFnZScgZGUgRUJJbWFnZQogIGltYWdlbiA8LSBJbWFnZShpbWFnZW5fcmVjb25zdHJ1aWRhLCBjb2xvcm1vZGUgPSAnQ29sb3InKQogIAogIHJldHVybihpbWFnZW4pCn0KCmBgYAoKKipJbWFnZW5lcyBzaW4gcnVpZG8qKgpgYGB7cixlY2hvPUZBTFNFfQojbWVtLm1heFZTaXplKDMyICogMTAyNF4zKSAKZm9yKGkgaW4gYygxOjMpKXsKcHJ1ZWJhPC1wcm9jZXNhcl9pbWFnZW5fd2F2ZWxldChmb3Rvc19jdWFkcmFkYXNbW2ldXSwgdGlwbyA9ICJoYXJkIiwgcG9saWN5ID0gInVuaXZlcnNhbCIpCnByaW50KG5hbWVzKGZvdG9zX2N1YWRyYWRhcylbaV0pCkVCSW1hZ2U6OmRpc3BsYXkocHJ1ZWJhLCBtZXRob2QgPSAncicsIHRpdGxlID0gJ0ltYWdlbiBXYXZlbGV0IHNpbiBydWlkbycpCn0KYGBgCgoKCgoKKlJ1aWRvIGdhdXNzaWFubyoKYGBge3IsZWNobz1GQUxTRX0KbGlicmFyeShtYWdpY2spCnBydWViYTwtcHJvY2VzYXJfaW1hZ2VuX3dhdmVsZXQoZm90b3NfY3VhZHJhZGFzW1sxXV0sIHRpcG8gPSAiaGFyZCIsIHBvbGljeSA9ICJ1bml2ZXJzYWwiKQppbWdfcmFzdGVyIDwtIGFzLnJhc3RlcihwcnVlYmEpCmltZ19tYWdpY2sgPC0gaW1hZ2VfcmVhZChpbWdfcmFzdGVyKQoKYGBgCgoKSW1hZ2VuIGNvbiBydWlkbyBHYXVzc2lhbm8gb3JpZ2luYWwKCjEuIEltYWdlbiBDb3J0YWRhIHNpbiBmaWx0cm8KCjIuIEFqdXN0YSBlbCBjb250cmFzdGUgZGUgbGEgaW1hZ2VuLCBpbmNyZW1lbnRhbmRvIGxhIGRpZmVyZW5jaWEgZW50cmUgbG9zIHDDrXhlbGVzIGNsYXJvcyB5IG9zY3Vyb3MuCgozLiBJbWFnZW4gY29uIGZpbHRybyBwYXNvIGFsdG8KCgpgYGB7cixlY2hvPUZBTFNFfQppbWFnZW5fY29ydGFkYTwtaW1hZ2VfY3JvcChpbWdfbWFnaWNrLCAiMTMwMHgxMjAwIikKCmtlcm5lbF9wYXNvX2FsdG88LW1hdHJpeChjKDAsLTEsMCwtMSw1LC0xLDAsLTEsMCksbnJvdz0zLG5jb2w9MykKCmZpbHRyYWRhX2FsdG88LWltYWdlX2NvbnZvbHZlKGltYWdlbl9jb3J0YWRhLGtlcm5lbF9wYXNvX2FsdG8pCgpjb25jb250cmFzdGU8LWltYWdlX2NvbnRyYXN0KGltYWdlbl9jb3J0YWRhKQoKaW1hZ2VuX2NvcnRhZGEgPC0gaW1hZ2VfYW5ub3RhdGUoaW1hZ2VuX2NvcnRhZGEsICJJbWFnZW4gQ29ydGFkYSIsIHNpemUgPSA1MCwgY29sb3IgPSAid2hpdGUiLCBsb2NhdGlvbiA9ICIrMTArMTAiKQpjb25jb250cmFzdGUgPC0gaW1hZ2VfYW5ub3RhdGUoY29uY29udHJhc3RlLCAiQ29uIENvbnRyYXN0ZSIsIHNpemUgPSA1MCwgY29sb3IgPSAid2hpdGUiLCBsb2NhdGlvbiA9ICIrMTArMTAiKQpmaWx0cmFkYV9hbHRvIDwtIGltYWdlX2Fubm90YXRlKGZpbHRyYWRhX2FsdG8sICJGaWx0cmFkYSBBbHRvIiwgc2l6ZSA9IDUwLCBjb2xvciA9ICJ3aGl0ZSIsIGxvY2F0aW9uID0gIisxMCsxMCIpCgojIENvbWJpbmFyIGxhcyBpbcOhZ2VuZXMgaG9yaXpvbnRhbG1lbnRlCmltYWdlbl9jb21iaW5hZGEgPC0gaW1hZ2VfYXBwZW5kKGMoaW1hZ2VuX2NvcnRhZGEsIGNvbmNvbnRyYXN0ZSwgZmlsdHJhZGFfYWx0bykpCgpFQkltYWdlOjpkaXNwbGF5KEltYWdlKGZvdG9zX2N1YWRyYWRhc1tbMV1dLCBjb2xvcm1vZGUgPSAnQ29sb3InKSwgbWV0aG9kID0gJ3InKQoKIyBNb3N0cmFyIGxhIGltYWdlbiBjb21iaW5hZGEKcHJpbnQoaW1hZ2VuX2NvbWJpbmFkYSkKYGBgCgoKCgojIyBGdW5jacOzbiBkZW5vaXNlLi4uCgojIENvbmNsdXNpb25lcwo=